package services

import (
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// LoggerService 日志服务
type LoggerService struct {
	logger      *zap.Logger
	logDir      string
	currentHour int
	mu          sync.RWMutex
	config      LoggerConfig
}

// LoggerConfig 日志配置
type LoggerConfig struct {
	Level      string `json:"level"`       // debug, info, warn, error
	Format     string `json:"format"`      // json, console
	MaxSize    int    `json:"max_size"`    // MB
	MaxAge     int    `json:"max_age"`     // days
	MaxBackups int    `json:"max_backups"` // 保留的备份文件数
	Compress   bool   `json:"compress"`    // 是否压缩
	LogDir     string `json:"log_dir"`     // 日志目录
	Rotation   bool   `json:"rotation"`    // 是否启用轮转
}

// DefaultLoggerConfig 默认日志配置
func DefaultLoggerConfig() LoggerConfig {
	return LoggerConfig{
		Level:      "info",
		Format:     "json",
		MaxSize:    100,
		MaxAge:     7,
		MaxBackups: 10,
		Compress:   true,
		LogDir:     "./logs",
		Rotation:   true,
	}
}

// NewLoggerService 创建新的日志服务
func NewLoggerService(config LoggerConfig) (*LoggerService, error) {
	// 确保日志目录存在
	if err := os.MkdirAll(config.LogDir, 0755); err != nil {
		return nil, fmt.Errorf("创建日志目录失败: %w", err)
	}

	ls := &LoggerService{
		logDir:      config.LogDir,
		currentHour: time.Now().Hour(),
		config:      config,
	}

	// 初始化日志器
	if err := ls.initLogger(); err != nil {
		return nil, fmt.Errorf("初始化日志器失败: %w", err)
	}

	// 启动日志轮转检查
	if config.Rotation {
		go ls.startRotationCheck()
	}

	return ls, nil
}

// initLogger 初始化日志器
func (ls *LoggerService) initLogger() error {
	ls.mu.Lock()
	defer ls.mu.Unlock()

	// 生成当前日志文件名
	logFileName := fmt.Sprintf("dcarpai-guider-%s.log", time.Now().Format("2006010215"))
	logFilePath := filepath.Join(ls.logDir, logFileName)

	// 配置日志级别
	level, err := zapcore.ParseLevel(ls.config.Level)
	if err != nil {
		level = zapcore.InfoLevel
	}

	// 配置编码器
	var encoderConfig zapcore.EncoderConfig
	var encoder zapcore.Encoder

	if ls.config.Format == "console" {
		encoderConfig = zap.NewDevelopmentEncoderConfig()
		encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
		encoder = zapcore.NewConsoleEncoder(encoderConfig)
	} else {
		encoderConfig = zap.NewProductionEncoderConfig()
		encoderConfig.TimeKey = "timestamp"
		encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
		encoder = zapcore.NewJSONEncoder(encoderConfig)
	}

	// 配置输出目标
	var writeSyncer zapcore.WriteSyncer

	// 文件输出
	logFile, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return fmt.Errorf("打开日志文件失败: %w", err)
	}

	// 同时输出到文件和控制台
	writeSyncer = zapcore.NewMultiWriteSyncer(
		zapcore.AddSync(logFile),
		zapcore.AddSync(os.Stdout),
	)

	// 创建核心
	core := zapcore.NewCore(encoder, writeSyncer, level)

	// 创建日志器
	ls.logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))

	return nil
}

// startRotationCheck 启动日志轮转检查
func (ls *LoggerService) startRotationCheck() {
	ticker := time.NewTicker(1 * time.Minute) // 每分钟检查一次
	defer ticker.Stop()

	for range ticker.C {
		currentHour := time.Now().Hour()
		if currentHour != ls.currentHour {
			ls.rotateLog()
			ls.currentHour = currentHour
		}
	}
}

// rotateLog 轮转日志
func (ls *LoggerService) rotateLog() {
	ls.logger.Info("开始日志轮转",
		zap.String("old_hour", fmt.Sprintf("%02d", ls.currentHour)),
		zap.String("new_hour", fmt.Sprintf("%02d", time.Now().Hour())),
	)

	// 重新初始化日志器
	if err := ls.initLogger(); err != nil {
		// 使用原日志器记录错误
		ls.logger.Error("日志轮转失败", zap.Error(err))
		return
	}

	ls.logger.Info("日志轮转完成")

	// 清理旧日志文件
	go ls.cleanupOldLogs()
}

// cleanupOldLogs 清理旧日志文件
func (ls *LoggerService) cleanupOldLogs() {
	if ls.config.MaxAge <= 0 {
		return // 不清理
	}

	cutoff := time.Now().AddDate(0, 0, -ls.config.MaxAge)

	entries, err := os.ReadDir(ls.logDir)
	if err != nil {
		ls.logger.Error("读取日志目录失败", zap.Error(err))
		return
	}

	cleaned := 0
	for _, entry := range entries {
		if entry.IsDir() {
			continue
		}

		// 检查文件名是否匹配日志文件格式
		if !ls.isLogFile(entry.Name()) {
			continue
		}

		info, err := entry.Info()
		if err != nil {
			continue
		}

		// 检查文件是否过期
		if info.ModTime().Before(cutoff) {
			filePath := filepath.Join(ls.logDir, entry.Name())
			if err := os.Remove(filePath); err != nil {
				ls.logger.Warn("删除过期日志文件失败",
					zap.String("file", entry.Name()),
					zap.Error(err),
				)
			} else {
				cleaned++
			}
		}
	}

	if cleaned > 0 {
		ls.logger.Info("清理过期日志文件",
			zap.Int("cleaned_count", cleaned),
			zap.Int("max_age_days", ls.config.MaxAge),
		)
	}
}

// isLogFile 检查是否为日志文件
func (ls *LoggerService) isLogFile(filename string) bool {
	return filepath.Ext(filename) == ".log" &&
		len(filename) >= 22 && // dcarpai-guider-2006010215.log 长度
		filename[:15] == "dcarpai-guider-"
}

// GetLogger 获取日志器
func (ls *LoggerService) GetLogger() *zap.Logger {
	ls.mu.RLock()
	defer ls.mu.RUnlock()
	return ls.logger
}

// LogOperation 记录操作日志
func (ls *LoggerService) LogOperation(operation, username, resource string, details map[string]interface{}) {
	fields := []zap.Field{
		zap.String("operation", operation),
		zap.String("username", username),
		zap.String("resource", resource),
		zap.Time("timestamp", time.Now()),
	}

	// 添加详细信息
	for key, value := range details {
		fields = append(fields, zap.Any(key, value))
	}

	ls.logger.Info("操作审计", fields...)
}

// LogLogin 记录登录日志
func (ls *LoggerService) LogLogin(username, ip, userAgent string, success bool) {
	fields := []zap.Field{
		zap.String("event_type", "login"),
		zap.String("username", username),
		zap.String("client_ip", ip),
		zap.String("user_agent", userAgent),
		zap.Bool("success", success),
		zap.Time("timestamp", time.Now()),
	}

	if success {
		ls.logger.Info("用户登录", fields...)
	} else {
		ls.logger.Warn("登录失败", fields...)
	}
}

// LogAPIAccess 记录API访问日志
func (ls *LoggerService) LogAPIAccess(method, path, username, ip string, statusCode int, duration time.Duration) {
	fields := []zap.Field{
		zap.String("event_type", "api_access"),
		zap.String("method", method),
		zap.String("path", path),
		zap.String("username", username),
		zap.String("client_ip", ip),
		zap.Int("status_code", statusCode),
		zap.Duration("duration", duration),
		zap.Time("timestamp", time.Now()),
	}

	if statusCode >= 200 && statusCode < 400 {
		ls.logger.Info("API访问", fields...)
	} else if statusCode >= 400 && statusCode < 500 {
		ls.logger.Warn("API访问错误", fields...)
	} else {
		ls.logger.Error("API访问失败", fields...)
	}
}

// GetLogStats 获取日志统计信息
func (ls *LoggerService) GetLogStats() (map[string]interface{}, error) {
	entries, err := os.ReadDir(ls.logDir)
	if err != nil {
		return nil, fmt.Errorf("读取日志目录失败: %w", err)
	}

	stats := map[string]interface{}{
		"log_dir":    ls.logDir,
		"file_count": 0,
		"total_size": int64(0),
		"files":      []map[string]interface{}{},
		"config":     ls.config,
	}

	for _, entry := range entries {
		if entry.IsDir() || !ls.isLogFile(entry.Name()) {
			continue
		}

		info, err := entry.Info()
		if err != nil {
			continue
		}

		fileInfo := map[string]interface{}{
			"name":     entry.Name(),
			"size":     info.Size(),
			"mod_time": info.ModTime().Format(time.RFC3339),
		}

		stats["files"] = append(stats["files"].([]map[string]interface{}), fileInfo)
		stats["file_count"] = stats["file_count"].(int) + 1
		stats["total_size"] = stats["total_size"].(int64) + info.Size()
	}

	return stats, nil
}

// UpdateConfig 更新日志配置
func (ls *LoggerService) UpdateConfig(newConfig LoggerConfig) error {
	ls.mu.Lock()
	defer ls.mu.Unlock()

	// 备份当前配置
	oldConfig := ls.config

	// 更新配置
	ls.config = newConfig

	// 如果日志目录变更，创建新目录
	if newConfig.LogDir != oldConfig.LogDir {
		if err := os.MkdirAll(newConfig.LogDir, 0755); err != nil {
			ls.config = oldConfig // 恢复配置
			return fmt.Errorf("创建新日志目录失败: %w", err)
		}
		ls.logDir = newConfig.LogDir
	}

	// 重新初始化日志器（如果级别或格式有变更）
	if newConfig.Level != oldConfig.Level || newConfig.Format != oldConfig.Format {
		if err := ls.initLogger(); err != nil {
			ls.config = oldConfig // 恢复配置
			return fmt.Errorf("重新初始化日志器失败: %w", err)
		}
	}

	ls.logger.Info("日志配置已更新",
		zap.Any("old_config", oldConfig),
		zap.Any("new_config", newConfig),
	)

	return nil
}

// Close 关闭日志服务
func (ls *LoggerService) Close() error {
	ls.mu.Lock()
	defer ls.mu.Unlock()

	if ls.logger != nil {
		return ls.logger.Sync()
	}
	return nil
}
